/*
 * Copyright (c) 2021-2023, WeiStudio
 *
 * License: Apache-2.0
 *
 * Change Logs:
 * Date           Author        Notes
 * 2021-07-13     WeiStudio      the first version
 */

#include "qe_core.h"
#include "qe_log.h"
#include "qe_service.h"
#if defined(_WIN32) || defined(__linux__)
#include "qe_platform.h"
#else
#include "qe_arch.h"
#endif
#include <stdarg.h>
#include <stdio.h>



static struct qelog_level __qe_level_tab[] = {
	{QE_NULL,	QELOG_NONE,		QE_LOG_COLOR_RESET},
	{"b",		QELOG_BURST,	QE_LOG_COLOR_RESET},
	{"t",		QELOG_TRACE,	QE_LOG_COLOR_WHITE},
	{"D",		QELOG_DEBUG,	QE_LOG_COLOR_GREEN_LIGHT},
	{"I",		QELOG_INFO,		QE_LOG_COLOR_CYAN_LIGHT},
	{"N",		QELOG_NOTICE,	QE_LOG_COLOR_YELLOW_LIGHT},
	{"W",		QELOG_WARN,		QE_LOG_COLOR_PINK_LIGHT},
	{"E",		QELOG_ERR,		QE_LOG_COLOR_RED_LIGHT},
	{"B",		QELOG_BUG,		QE_LOG_COLOR_RED},
};

static struct qelog_context logctx = {
	.level = QELOG_INFO,
	.features = QELOG_F_MODEL | QELOG_F_LEVEL | QELOG_F_TIME,
	.output = QE_NULL,
    .ring.buf = QE_NULL,
    .f_initialized = 0,
};

static qe_bool_t level_visible(qe_u8 level)
{
	return (logctx.level <= level);
}

void qelog_init(qe_u8 level, qe_u8 features)
{
    if (logctx.f_initialized)
        return;

    if (level < QELOG_DISABLE)
	    logctx.level = level;
    
    if (features)
        logctx.features = features;
    
    logctx.f_initialized = 1;
}

void qelog_set_level(qe_u8 level)
{
	logctx.level = level;
}

void qelog_set_features(qe_u8 features)
{
	logctx.features = features;
}

qe_ringbuffer_t *qelog_get_buffer(void)
{
    return &logctx.ring;
}

qe_err_t qelog_set_buffer(void *buf, qe_u32 size)
{
	if (logctx.ring.buf)
		return qe_err_exist;
	
	qe_ringbuffer_init(&logctx.ring, buf, size);
	return qe_ok;
}

void qelog_set_output(void (*output)(char *buf, int len))
{
	logctx.output = output;
}

void qelog(qe_u8 level, const char *mname, 
    const char *func, int line, const char *fmt, ...)
{
	qe_s32 n;
	qe_u32 now;
	va_list args;
	char buffer[QELOG_BFZS] = {'\0'};

	if (!level_visible(level)) {return;}

	qe_strb_t strb = qe_strb_init(buffer, QELOG_BFZS);
	struct qelog_level *lp = &__qe_level_tab[level];

	/* color start */
	if (logctx.features & QELOG_F_COLOR) {
		qe_strb_format(strb, "%s", lp->color);
	}

	if (logctx.features & QELOG_F_TIME) {
		int sec, min, hour;
		sec = 0;
		min = 0;
		hour = 0;
		now = qe_time();
		if (now >= 3600) {
			hour = now/3600;
			now = now % 3600;
		}
		if (now >= 60) {
			min = now/60;
			now = now % 60;
		}
		sec = now;
		qe_strb_format(strb, "[%02i:%02i:%02i]", hour, min, sec);
	}

	if (logctx.features & QELOG_F_MODEL)
		qe_strb_format(strb, "[%-"QELOG_MODEL_STR(QELOG_MODEL_MAX)"s][%s]",
		//qe_strb_format(strb, "[%-5s][%s]",
			mname, lp->string);
	else
		qe_strb_format(strb, "[%s]", lp->string);

    if (logctx.features & QELOG_F_FUNC)
        qe_strb_format(strb, "[%s:%d]", func, line);

    qe_strb_string(strb, " ");

	va_start(args, fmt);
	n = qe_vsnprintf(strb.p, strb.max, fmt, args);
	va_end(args);

	strb.p += n;
	strb.max -= n;
	strb.len += n;

	/* color end */
	if (logctx.features & QELOG_F_COLOR) {
		qe_strb_format(strb, "%s", QE_LOG_COLOR_RESET);
	}

	qe_strb_string(strb, "\r\n");

	if (logctx.output)
		logctx.output(strb.head, strb.len);
#ifndef CONFIG_BUILD_BERAMETAL
    else
        printf("%.*s", strb.len, strb.head);
#endif

    if (logctx.ring.buf && logctx.features & QELOG_F_RING)
        qe_ringbuffer_write(&logctx.ring, strb.head, strb.len);
}

void qe_hexdump(qe_u8 level, const char *func, int linenr, const void *vbuf, qe_size_t len)
{
	unsigned int n;
	unsigned char *buf = (unsigned char *)vbuf;
	
	if (!level_visible(level))
		return;

	if (!len || !vbuf)
		return;

	qelog(level, "hex", func, linenr, "");

	for (n = 0; n < len;) {
		unsigned int start = n, m;
		char line[128], *p = line;

		p += qe_snprintf(p, 10, "%04X: ", start);
		for (m = 0; m < 16 && n < len; m++)
			p += qe_snprintf(p, 5, "%02X ", buf[n++]);

		while (m++ < 16)
			p += qe_snprintf(p, 5, "   ");

		p += qe_snprintf(p, 6, "   ");
		for (m = 0; m < 16 && (start + m) < len; m++) {
			if (buf[start + m] >= ' ' && buf[start + m] < 127)
				*p++ = buf[start + m];
			else
				*p++ = '.';
		}

		while (m++ < 16)
			*p++ = ' ';

		*p = '\0';
		
		qelog(level, "hex", func, linenr, "%s", line);
		(void)line;
	}	

	qelog(level, "hex", func, linenr, "");
}

void qe_bitdump(qe_u8 level, const char *func, int linenr, const void *vbuf, qe_size_t len)
{
	unsigned int n;
	qe_u32 *buf = (qe_u32 *)vbuf;

    if (!level_visible(level))
		return;
    
	if (!len || !vbuf)
		return;
    
    qelog(level, "bit", func, linenr, "");

    for (n=0; n<len; n+=32) {
		unsigned int start = n, m;
		char line[80], *p = line;

		p += qe_snprintf(p, 10, "%04d:  ", start);
		for (m = 0; m < 32 && n < len; m++)
			p += qe_snprintf(p, 5, "%d ", (buf[n/32]&(1<<m))>0);
		while (m++ < 32)
			p += qe_snprintf(p, 5, "   ");
		
		p += qe_snprintf(p, 5, "   ");
		*p = '\0';
		qelog(level, "bit", func, linenr, "%s", line);
		(void)line;
	}

    qelog(level, "bit", func, linenr, "");
}

